﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Usart_Updata
{
    public struct JoinBin_Typ
    {
        /// <summary>
        /// 原文件长度
        /// </summary>
        public UInt32 FileSize;

        /// <summary>
        /// 加密后的文件数据
        /// </summary>
        public byte[] FileBuff;
    }

    public partial class Form1 : Form
    {
        List<JoinBin_Typ> UpdataList = new List<JoinBin_Typ>();
        BackgroundWorker backgroundWorker = new BackgroundWorker();
        SerialPort serialPort = new SerialPort();
        public Form1()
        {
            InitializeComponent();
            comboBox1_DropDown(null, null);  //扫描串口
            backgroundWorker.WorkerReportsProgress = true;
            backgroundWorker.WorkerSupportsCancellation = true;
            backgroundWorker.ProgressChanged += Worker_ProgressChanged;
            backgroundWorker.RunWorkerCompleted += Worker_RunWorkerCompleted;
            backgroundWorker.DoWork += Worker_DoWork;
        }




        private void button2_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Title = "选择固件文件";
            openFileDialog1.Filter = "加密拼接(*.joinBin)|*.joinBin";
            openFileDialog1.CheckPathExists = true;

            if (openFileDialog1.ShowDialog() == DialogResult.OK)   //如果点击确定
            {
                textBox2.Text = "";
                UpdataList.Clear();
                try
                {
                    StreamReader streamReader = new StreamReader(openFileDialog1.FileName);
                    string str = streamReader.ReadToEnd();
                    streamReader.Close();
                    byte[] FileBytes = Convert.FromBase64String(str);
                    byte[] TempBuff = new byte[FileBytes.Length - 4]; //去掉CRC后的数据 拼接的原始数据
                    Array.Copy(FileBytes, TempBuff, TempBuff.Length);
                    if (((UInt32)BitConverter.ToInt32(FileBytes, FileBytes.Length - 4)) != CCRC32_0X04C11DB7.Get_CRC32_ETH_STM32(0XFFFFFFFF, TempBuff))
                    {
                        MessageBox.Show("固件文件错误");
                        return;
                    }
                    JoinBin_Typ joinBin_Typ = new JoinBin_Typ();
                    UInt32 File0Szie = 0;
                    File0Szie = (UInt32)BitConverter.ToInt32(FileBytes, 0); //第一个文件的长度
                    joinBin_Typ.FileSize = File0Szie;   //原文件数据长度
                    Debug.WriteLine("第一个文件长度:" + File0Szie);
                    if (File0Szie > 0)
                    {
                        File0Szie = (UInt32)((File0Szie + 0X0F) & (~0X0F));  //得到文件的长度
                        joinBin_Typ.FileBuff = new byte[File0Szie];
                        Array.Copy(TempBuff, 4, joinBin_Typ.FileBuff, 0, joinBin_Typ.FileBuff.Length);
                    }
                    else  //长度为空
                    {
                        joinBin_Typ.FileBuff = new byte[0];
                    }
                    UpdataList.Add(joinBin_Typ);
                    Debug.WriteLine("第一个文件: " + CHardwareInfo.ToHexString(UpdataList[0].FileBuff));

                    UInt32 File1Szie = 0;
                    File1Szie = (UInt32)BitConverter.ToInt32(FileBytes, (int)(4 + File0Szie)); //第二个文件的长度
                    joinBin_Typ.FileSize = File1Szie;   //原文件数据长度
                    Debug.WriteLine("第二个文件长度:" + File1Szie);
                    if (File1Szie > 0)
                    {
                        File1Szie = (UInt32)((File1Szie + 0X0F) & (~0X0F));  //得到文件的长度
                        joinBin_Typ.FileBuff = new byte[File1Szie];
                        Array.Copy(TempBuff, 4 + File0Szie + 4, joinBin_Typ.FileBuff, 0, joinBin_Typ.FileBuff.Length);
                    }
                    else  //长度为空
                    {
                        joinBin_Typ.FileBuff = new byte[0];
                    }
                    UpdataList.Add(joinBin_Typ);
                    Debug.WriteLine("第二个文件: " + CHardwareInfo.ToHexString(UpdataList[1].FileBuff));
                    textBox2.Text = openFileDialog1.FileName;   //显示文件路径
                }
                catch (Exception ex)
                {
                    textBox2.Text = "";
                    UpdataList.Clear();
                    MessageBox.Show("固件文件错误 " + ex.Message);
                }
            }
        }

        private void comboBox1_DropDown(object sender, EventArgs e)
        {
            string[] PortName = CHardwareInfo.GetPcSerialPortName(); //得到当前设备上的串口号         
            comboBox1.Items.Clear();
            if (PortName == null)
            {
                MessageBox.Show("没有找到串口");
                return;
            }
            comboBox1.Items.AddRange(PortName);
            string text = comboBox1.Text;
            if (text == "") //如果之前的数据为空
            {
                comboBox1.SelectedIndex = (comboBox1.Items.Count > 0) ? 0 : comboBox1.SelectedIndex;
            }
            else//如果之前的数据不为空
            {
                int Count = 0;
                Count = comboBox1.Items.IndexOf(text);
                comboBox1.SelectedIndex = Count;
            }
        }

        /// <summary>
        /// 单击更新固件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                if (UpdataList.Count != 2)
                {
                    MessageBox.Show("请选择固件文件");
                    return;
                }
                if ((UpdataList[0].FileSize == 0) && (UpdataList[1].FileSize == 0))
                {
                    MessageBox.Show("为空文件");
                    return;
                }
                if (backgroundWorker.IsBusy == false) //如果当前没有执行固件升级
                {
                    serialPort.PortName = CHardwareInfo.GetPcSerialPortNameCom(comboBox1.Text);   //得到串口号
                    int Baud = 0;
                    if (int.TryParse(textBox1.Text, NumberStyles.Any, null, out Baud) == false)
                    {
                        MessageBox.Show("波特率输入错误 请输入10进制");
                        return;
                    }
                    Debug.WriteLine("PortName: " + serialPort.PortName + "Baud: " + Baud);
                    serialPort.BaudRate = Baud;  //得到波特率
                    serialPort.Open();   //打开串口
                    Set_progressBar_Val(0);
                    backgroundWorker.RunWorkerAsync(UpdataList);
                    button1.Text = "停止升级";
                }
                else   //手动停止
                {
                    backgroundWorker.CancelAsync();   //请求停止固件升级
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void User_UI_Init(bool OFF_ON)
        {
            if (InvokeRequired == true)   //在不同线程上调用的 必须通过 Invoke 方法对控件进行调用  
            {
                Invoke(new Action<bool>(User_UI_Init), new object[] { OFF_ON });
            }
            else
            {
                groupBox1.Enabled = OFF_ON;
                textBox1.Enabled = OFF_ON;
                comboBox1.Enabled = OFF_ON;
            }
        }


        #region 传输功能函数
        /// <summary>
        /// 求和
        /// </summary>
        /// <param name="Databuff"></param>
        /// <returns></returns>
        private UInt16 GetAdd_Bytes(byte[] Databuff)
        {
            UInt16 AddCount = 0;
            foreach (var item in Databuff)
            {
                AddCount += item;
            }
            return AddCount;
        }

        /// <summary>
        /// 把整形转换成字节数组  高字节在前
        /// </summary>
        /// <param name="Val"></param>
        /// <returns></returns>
        private byte[] Get_UInt_To_Bytes(UInt16 Val)
        {
            byte[] temp = new byte[] { (byte)(Val >> 8), (byte)Val };
            return temp;
        }

        private byte[] Get_UInt_To_Bytes(UInt32 Val)
        {
            byte[] temp = new byte[] { (byte)(Val >> 24), (byte)(Val >> 16), (byte)(Val >> 8), (byte)Val };
            return temp;
        }

        /// <summary>
        /// 串口发送数据并接收数据
        /// </summary>
        /// <param name="SendBuff"> 发送的数据缓存</param>
        /// <param name="RecBuff">接收数据的缓存</param>
        /// <param name="TimeOver">超时时间</param>
        /// <returns>0操作成功  其它错误信息 1 串口关闭 2 接收超时 </returns>
        private int USART_SEND_REC_Bytes(byte[] SendBuff, ref byte[] RecBuff, int TimeOver = 2 * 1000)
        {
            lock (serialPort)
            {
                if (serialPort.IsOpen == false)
                {
                    return 1;
                }
                try
                {
                    byte[] TempRecBuff = new byte[2048];
                    Debug.WriteLine("发送数据{0}个字节：\r\n" + CHardwareInfo.ToHexString(SendBuff), SendBuff.Length);
                    serialPort.Write(SendBuff, 0, SendBuff.Length);  //发送
                    do
                    {
                        var IAs = serialPort.BaseStream.BeginRead(TempRecBuff, 0, TempRecBuff.Length, null, null);
                        if (IAs.AsyncWaitHandle.WaitOne(TimeOver, false) == true) //堵塞接收到信号
                        {
                            int Length = serialPort.BaseStream.EndRead(IAs);  //得到此次接收到的数据
                            Thread.Sleep(5);   //等待串口缓存区接收数据完成
                            int BytesLength = serialPort.BytesToRead;  //得到串口缓存区数据
                            serialPort.Read(TempRecBuff, Length, BytesLength);
                            RecBuff = new byte[Length + BytesLength];
                            Array.Copy(TempRecBuff, RecBuff, RecBuff.Length);
                            //打印调试信息 --------------------------
                            Debug.WriteLine("单次接收数据长度{0}：\r\n" + CHardwareInfo.ToHexString(RecBuff), RecBuff.Length);
                            //-----------------------------------------
                            return 0;
                        }
                        else
                        {
                            return 2;
                        }
                    } while (true);
                }
                catch (Exception)
                {
                    return 1;
                }
            }
        }
        readonly static byte USER_HEAD = 0XAA;
        readonly static UInt16 DATAFrameLen = 1024;
        /// <summary>
        /// 串口传输协议  
        /// </summary>
        /// <param name="SendBuff"></param>
        /// <returns> 0成功 1串口关闭  2接收超时  3下位机主动停止  4 超过最大重传次数  5 不遵循协议 </returns>
        private int USART_Protocol_Trans(byte[] SendBuff)
        {
            int Ret_STA = 0;
            UInt16 CRC_DATA = GetAdd_Bytes(SendBuff);
            CRC_DATA += (UInt16)USER_HEAD;
            byte[] Temp_Sendbuff = new byte[1 + SendBuff.Length + 2];
            Temp_Sendbuff[0] = USER_HEAD;  //头
            Array.Copy(SendBuff, 0, Temp_Sendbuff, 1, SendBuff.Length); //协议数据
            Array.Copy(Get_UInt_To_Bytes(CRC_DATA), 0, Temp_Sendbuff, Temp_Sendbuff.Length - 2, 2); //添加CRC
            byte Request_Tpy = (byte)(Temp_Sendbuff[1] + 1);  //希望得到的请求类型
            byte Ret_Sta = (Request_Tpy == ((byte)0X05)) ? Temp_Sendbuff[2] : (byte)0X00;
            UInt16 ErrCount = 5;
            byte[] RecBuff = new byte[1];
            do
            {
                if ((Ret_STA = USART_SEND_REC_Bytes(Temp_Sendbuff, ref RecBuff)) != 0)  //如果发送失败
                {
                    return Ret_STA;
                }
                //长度  头  请求的类型  
                if ((RecBuff.Length == 3) && (RecBuff[0] == USER_HEAD) && (RecBuff[1] == Request_Tpy))
                {
                    if (RecBuff[2] == Ret_Sta)  //如果返回正确的应答
                    {
                        return 0;
                    }
                    else if ((Request_Tpy != 0X05) && (RecBuff[2] > (byte)0X01))  //如果不是数据帧  则其它帧可以直接停止传输
                    {
                        return 3;    //手动停止传输
                    }
                }
                else  //如果不是按协议走  则直接返回错误
                {
                    return 5;
                }
            } while ((--ErrCount) > 0);
            return 4;   //超过最大重传次数
        }
        private static string GetErrString(int Errcode)
        {
            string str = "";
            switch (Errcode)
            {
                case 0:
                    str = "操作成功";
                    break;
                case 1:
                    str = "串口被关闭";
                    break;
                case 2:
                    str = "从机未应答";
                    break;
                case 3:
                    str = "下位机主动停止";
                    break;
                case 4:
                    str = "超过最大重传次数";
                    break;
                case 5:
                    str = "不遵循协议";
                    break;
                default:
                    str = "未知错误";
                    break;
            }
            Debug.WriteLine(str);
            return str;
        }
        readonly string HandshakeStr = "aBS!12";
        private void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            User_UI_Init(false);
            int ErrCode = 0;
            BackgroundWorker bgWorker = sender as BackgroundWorker;
            List<JoinBin_Typ> JoinBinlist = e.Argument as List<JoinBin_Typ>;
            JoinBin_Typ joinBin_Typ;
            UInt32 AllFileSize = (UInt32)(JoinBinlist[0].FileBuff.Length + JoinBinlist[1].FileBuff.Length);  //要发送数据的总长度
            UInt32 AllFileOffset = 0;  //当前发送文件的偏移
            bgWorker.ReportProgress((int)AllFileOffset, AllFileSize);   //触发状态变更
            byte[] Protocol_Template = new byte[] { 0X00, 0X00, 0XFF, 0X00, 0X00 };
            UInt16 index = 0;
            string UserAsc = "";   //自定义握手字符串
            UserAsc = HandshakeStr;  //自定义握手字符串
            Debug.WriteLine("UserAsc: " + UserAsc);
            byte[] UserBuff = Encoding.Default.GetBytes(UserAsc);
            //发送握手包
            byte[] TranBuff = new byte[5 + UserBuff.Length];
            Array.Copy(Protocol_Template, 0, TranBuff, 0, Protocol_Template.Length);
            Array.Copy(Get_UInt_To_Bytes((UInt16)UserBuff.Length), 0, TranBuff, Protocol_Template.Length - 2, 2); //填充数据域长度
            Array.Copy(UserBuff, 0, TranBuff, Protocol_Template.Length, UserBuff.Length);  //填充自定义字符串
            if ((ErrCode = USART_Protocol_Trans(TranBuff)) != 0) //发送握手帧
            {
                e.Result = GetErrString(ErrCode);
                return;
            }
            if (bgWorker.CancellationPending == true)  //如果用户终止
            {
                e.Cancel = true;
                return;
            }
            for (index = 0; index < (UInt16)JoinBinlist.Count; index++)
            {
                joinBin_Typ = JoinBinlist[index];
                if (joinBin_Typ.FileSize > 0)  //文件数据不为空
                {
                    //发送起始包
                    TranBuff = new byte[5 + 6];
                    Array.Copy(Protocol_Template, 0, TranBuff, 0, Protocol_Template.Length); //拷贝模板
                    TranBuff[0] = 0X02;  //起始帧
                    Array.Copy(Get_UInt_To_Bytes(6), 0, TranBuff, Protocol_Template.Length - 2, 2); //填充数据域长度
                    Array.Copy(Get_UInt_To_Bytes(index), 0, TranBuff, Protocol_Template.Length, 2); //填充索引号
                    Array.Copy(Get_UInt_To_Bytes(joinBin_Typ.FileSize), 0, TranBuff, Protocol_Template.Length + 2, 4); //填充文件的明文大小
                    if ((ErrCode = USART_Protocol_Trans(TranBuff)) != 0) //发送起始帧
                    {
                        e.Result = GetErrString(ErrCode);
                        return;
                    }
                    if (bgWorker.CancellationPending == true)  //如果用户终止
                    {
                        e.Cancel = true;
                        return;
                    }
                    List<byte[]> TranDataList = new List<byte[]>();   //要发送的数据帧 LIST
                    for (int i = 0; i < joinBin_Typ.FileBuff.Length;)
                    {
                        UInt16 length = (UInt16)Math.Min(joinBin_Typ.FileBuff.Length - i, DATAFrameLen);
                        byte[] temp = new byte[length];
                        Array.Copy(joinBin_Typ.FileBuff, i, temp, 0, temp.Length);  //拷贝
                        TranDataList.Add(temp);
                        i += length;
                    }
                    if ((joinBin_Typ.FileBuff.Length % DATAFrameLen) == 0)  //如果长度正好是整数倍  则添加空数据
                    {
                        TranDataList.Add(new byte[0]);
                    }
                    byte FranNumber = 0X00;
                    for (UInt16 i = 0; i < (UInt16)TranDataList.Count; i++)
                    {
                        TranBuff = new byte[5 + TranDataList[i].Length];
                        Array.Copy(Protocol_Template, 0, TranBuff, 0, Protocol_Template.Length); //拷贝模板
                        TranBuff[0] = 0X04;  //数据帧
                        TranBuff[1] = FranNumber;   //帧号
                        TranBuff[2] = (byte)(TranBuff[1] ^ 0XFF);
                        FranNumber++;
                        Array.Copy(Get_UInt_To_Bytes((UInt16)TranDataList[i].Length), 0, TranBuff, Protocol_Template.Length - 2, 2); //填充数据域长度
                        Array.Copy(TranDataList[i], 0, TranBuff, Protocol_Template.Length, TranDataList[i].Length); //填充数据域
                        if ((ErrCode = USART_Protocol_Trans(TranBuff)) != 0) //发送数据帧
                        {
                            e.Result = GetErrString(ErrCode);
                            return;
                        }
                        if (bgWorker.CancellationPending == true)  //如果用户终止
                        {
                            e.Cancel = true;
                            break;
                        }
                        AllFileOffset += (UInt32)TranDataList[i].Length;
                        bgWorker.ReportProgress((int)AllFileOffset, AllFileSize);   //触发状态变更
                    }
                }
            }
            e.Result = "固件升级完成";
        }

        /// <summary>
        /// 线程状态变更
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            UInt32 AllFileSize = (UInt32)e.UserState;
            UInt32 AllFileOffset = (UInt32)e.ProgressPercentage;
            Debug.Write("文件传输总大小: " + AllFileSize + "   当前已传输: " + AllFileOffset);
            UInt16 Val = (UInt16)((AllFileOffset * 10000) / AllFileSize);
            //修改进度条的显示。
            Set_progressBar_Val(Val);
        }

        /// <summary>
        /// 线程执行完成 或者终止
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }
            }
            catch (Exception)
            {
            }

            User_UI_Init(true);
            button1.Text = "固件升级";
            //如果用户手动取消了当前操作
            if (e.Cancelled)
            {
                MessageBox.Show("手动停止固件更新");
                return;
            }
            string str = e.Result as string;
            MessageBox.Show(str);
        }

        /// <summary>
        /// 显示进度
        /// </summary>
        /// <param name="Val"></param>
        private void Set_progressBar_Val(UInt16 Val)
        {
            progressBar1.Value = Val;
            label2.Text = "" + (Val / 100) + "." + (Val % 100).ToString("d02") + "%";
        }
        #endregion


        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync();
            }
        }
    }
}
